// naypr2_SampleConsole.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "..\napyr2\napyr2.h"

static char CURRENT_VERSION[] = "1.0";

using namespace napyr2;

PythonRuntime runtime;

int test_function1(int default_value = 0)
{
	PythonCall pc(&runtime, "test_function_with_parameters");
	pc.Add(rand());
	pc.Add(rand());
	pc.Call(default_value);
	return default_value;
}

void generate_archive(const char* directory)
{
	PythonCall pc(&runtime, "generate_archive");
	pc.Add(directory);
	pc.Call();
}

void test_python_threading(int n)
{
	PythonCall pc(&runtime, "test_python_threading");
	pc.Add(n);
	pc.Call();
}

PythonObject* PyTrace(const char* s)
{
	printf("PyTrace called, yeah: %s\r\n", s);
	return NULL;
}

DWORD g_dwGlobal = 0;

PythonObject* inc(const char* s)
{
	++g_dwGlobal;
	for( int i = 0; i < 10; ++i )
	{
		printf("%s in %ld\r\n", s, GetCurrentThreadId() );
		Sleep(rand() % 10);
		test_function1(i);
	}

	return NULL;
}

PythonObject* dec(const char* s)
{
	--g_dwGlobal;
	printf("%s in %ld\r\n", s, GetCurrentThreadId() );
	Sleep(rand() % 10);
	
	return NULL;
}

class SampleConsole
{
public:
	SampleConsole();
	virtual ~SampleConsole();

private:
	SampleConsole( const SampleConsole& objectSrc );
	SampleConsole& operator=( const SampleConsole& objectSrc );

public:
	int Run(int argc, _TCHAR* argv[]);

private:
	bool HandleInputArgs(int argc, _TCHAR* argv[]);
	bool ShowHelp(LPCSTR arg);

private:
	std::string m_strPythonPath;
	std::string m_strStartupScript;
	std::string m_strTargetDirectory;
	bool m_bDumpBin;
};

SampleConsole::SampleConsole()
	:	m_strPythonPath("dist\\python-distribution.zip"),
		m_strStartupScript("main_script"),
		m_strTargetDirectory("dist"),
		m_bDumpBin(false)
{
}

SampleConsole::~SampleConsole()
{
}



int SampleConsole::Run(int argc, _TCHAR* argv[])
{

	printf("NAPYR2 Sample Console - Version %s - March 13, 2014\r\n", CURRENT_VERSION);
	printf("Freeware written by Gerson Kurz (http://p-nand-q.com)\r\n\r\n");
	
	if( !HandleInputArgs(argc, argv) )
		return 10;

	PythonModule builtin("builtin");
	builtin.Add( "PyTrace", &PyTrace );
	builtin.Add( "inc", &inc );
	builtin.Add( "dec", &dec );

	// this is how you can define variables for the module
	builtin.Define("__VERSION__", CURRENT_VERSION);
	
	printf("Attempting to start Python:\r\nPath: '%s'\r\nMain: '%s'\r\n\r\n",
		m_strPythonPath.c_str(), m_strStartupScript.c_str());

	if( runtime.Startup( m_strPythonPath.c_str(), m_strStartupScript.c_str(), &builtin ) )
	{
		printf("OK, Python started successfully...\r\n");
		
		if( m_bDumpBin )
		{
			generate_archive(m_strTargetDirectory.c_str());
		}
		else
		{
			/*
			printf("TEST: ");
			for( int i = 0; i < 10; ++i )
			{
				int value = test_function1();
				printf("%d, ", value);
			}
			*/

			test_python_threading(10);

			printf("... success: g_dwGlobal = %ld\r\n", g_dwGlobal);
		}

		runtime.Cleanup();
	}
	else
	{
		printf("ERROR, unable to start Python: hit the panic button!!!!");
	}
	return 0;
}

typedef enum {
	HIA_EXPECT_ARGUMENT,
	HIA_EXPECT_STARTUP_SCRIPT,
	HIA_EXPECT_STARTUP_PATH,
	HIA_EXPECT_TARGET_PATH,
} HIA_EXPECTED_ARG;


bool SampleConsole::ShowHelp(LPCSTR arg)
{
	printf("USAGE: %s [OPTIONS]\r\n", arg);
	printf("OPTIONS:\r\n");
	printf("/DUMPBIN .............. create binary archive\r\n");
	printf("/PATH name ............ startup path (defaults to %s)\r\n", m_strPythonPath.c_str());
	printf("/MAIN name ............ startup script (defaults to %s)\r\n", m_strStartupScript.c_str());
	printf("/TARGET name .......... target path (defaults to %s)\r\n", m_strTargetDirectory.c_str());
	return false;
}

inline bool IsSameStringI(const char* a, const char* b)
{
	if( a == NULL )
		return (b == NULL);

	if( b == NULL )
		return false;

	return _stricmp(a, b) == 0;
}

bool SampleConsole::HandleInputArgs(int argc, _TCHAR* argv[])
{
	HIA_EXPECTED_ARG state = HIA_EXPECT_ARGUMENT;

	for( int i = 1; i < argc; ++i )
	{
		LPCSTR arg = argv[i];

		switch(state)
		{
		case HIA_EXPECT_STARTUP_SCRIPT:
			m_strStartupScript = arg;
			state = HIA_EXPECT_ARGUMENT;
			break;

		case HIA_EXPECT_STARTUP_PATH:
			m_strPythonPath = arg;
			state = HIA_EXPECT_ARGUMENT;
			break;

		case HIA_EXPECT_TARGET_PATH:
			m_strTargetDirectory = arg;
			state = HIA_EXPECT_ARGUMENT;
			break;

		case HIA_EXPECT_ARGUMENT:
			{
				LPCSTR decoded_arg = NULL;
				if( (arg[0] == '/') )
				{
					decoded_arg = arg+1;
				}
				else if( (arg[0] == '-') )
				{
					if( arg[1] == '-' )
					{
						decoded_arg = arg+2;
					}
					else
					{
						decoded_arg = arg+1;
					}
				}
				if( !decoded_arg )
				{
					printf("ERROR: unknown argument '%s'\r\n\r\n", arg);
					return ShowHelp(argv[0]);
				}
				
				if( IsSameStringI(decoded_arg, "dumpbin") )
				{
					m_bDumpBin = true;
				}
				else if( IsSameStringI(decoded_arg, "path") )
				{
					state = HIA_EXPECT_STARTUP_PATH;
				}
				else if( IsSameStringI(decoded_arg, "main") )
				{
					state = HIA_EXPECT_STARTUP_SCRIPT;
				}
				else if( IsSameStringI(decoded_arg, "target") )
				{
					state = HIA_EXPECT_TARGET_PATH;
				}
				else if( IsSameStringI(decoded_arg, "help") || IsSameStringI(decoded_arg, "?") )
				{
					return ShowHelp(argv[0]);
				}
				else 
				{
					printf("ERROR: unknown argument '%s'\r\n\r\n", arg);
					return ShowHelp(argv[0]);
				}
			}
			break;
		}

	}
	return true;
}


int _tmain(int argc, _TCHAR* argv[])
{
	srand((unsigned int) time(NULL));

	return SampleConsole().Run(argc, argv);
}